package net.facelib.jni;

import org.apache.commons.pool2.PooledObjectFactory;
import org.apache.commons.pool2.impl.GenericObjectPool;
import org.apache.commons.pool2.impl.GenericObjectPoolConfig;

import com.gitee.l0km.com4j.base.BaseVolatile;
import com.gitee.l0km.com4j.base.ILazyInitVariable;

import static net.facelib.jni.BridgeLog.BRIDEG_LOGGER;
import java.lang.ref.WeakReference;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Map.Entry;
import static com.google.common.base.Preconditions.checkNotNull;

/**
 * 资源池封装
 * 基于commons-pools将非线程安全的{@link BaseJniBridge}实例用资源池{@link GenericObjectPool}管理,
 * 每次调用前从资源池申请一个{@link BaseJniBridge}实例来使用，用完后归还资源池，
 * 以实现对{@link BaseJniBridge}实例线程安全的调用
 * @author guyadong
 *
 * @param <T> 资源池类型
 */
public class BridgePool<T extends BaseJniBridge> {
	/**
	 * 同步锁对象
	 */
	private volatile static SyncLock syncLock = SyncLock.NULL_LOCK;
	/**
	 * 全局资源池对象,保存所有资源池对象
	 */
	private static final Hashtable<Class<?>,WeakReference<? extends BridgePool<?>>> bridgePools = new Hashtable<>();

	private static final boolean jmxEnable = isJmxEnable();
	/**
	 * 判断JVM是否支持JMX，android下返回{@code false}
	 * @return
	 */
	private static boolean isJmxEnable(){
		try{
			Class.forName("java.lang.management.ManagementFactory");
			return true;
		}catch(ClassNotFoundException e){
			return false;
		}
	}
	private final ThreadLocal<T> localInstance = new ThreadLocal<>();
	private final BaseInstanceFactory<T> factory;
	private final GenericObjectPoolConfig config;
	/**
	 * 资源池<br>
	 * 每一次执行SDK调用时，要从资源池从申请一个资源实例，调用结束后归还到资源池，
	 * 实例的初始化和销毁都由资源负责，调用者不须负责资源实例的初始化与释放
	 */
	private final ILazyInitVariable<GenericObjectPool<T>> lazyPool = new BaseVolatile<GenericObjectPool<T>>(){
	
		@Override
		protected GenericObjectPool<T> doGet() {
			return new GenericObjectPool<>(factory,config);
		}};
		
	/**
	 * 构造方法
	 * @param type 资源类型
	 * @param config 资源池配置参数对象
	 * @param factory 资源实例创建工厂
	 */
	public BridgePool(Class<T> type,GenericObjectPoolConfig config,BaseInstanceFactory<T> factory) {
		// android平台不支持JMX,所以这里必须设置为false,否则抛出异常
		this.config = checkNotNull(config,"config is null");
		this.config.setJmxEnabled(jmxEnable);
		this.factory = checkNotNull(factory,"factory is null");
		// 当前实例添加到全局资源池对象
		bridgePools.put(type, new WeakReference<>(this));
	}
	/**
	 * 构造方法
	 * @param type 资源类型
	 * @param config 资源池配置参数对象
	 */
	public BridgePool(Class<T> type,GenericObjectPoolConfig config) {
		this(type, config, new BaseInstanceFactory<>(type));
	}
	/**
	 * 从资源池申请一个实例<br>
	 * 如果调用了{@link #bindInstanceForCurentThread()}则直接使用当前线程绑定的实例
	 * @return 申请的实例
	 */
	public T applyInstance() {
		syncLock.checkSyncLock();
		if(null != localInstance.get()){
			return localInstance.get();
		}
		try {
			GenericObjectPool<T> pool = lazyPool.get();
			BRIDEG_LOGGER.log("apply instance {},c:{},d:{},b:{} r:{},i:{},a:{}",
					factory.getType().getName(),
					pool.getCreatedCount(),
					pool.getDestroyedCount(),
					pool.getBorrowedCount(),
					pool.getReturnedCount(),
					pool.getNumIdle(),
					pool.getNumActive());
			return pool.borrowObject();
		} catch(RuntimeException e){
			throw e;
		}catch (Exception e) {
			throw new RuntimeException(e);
		}
	}
	/**
	 * 归还实例到资源池<br>
	 * 如果当前线程绑定了实例,则需要调用{@link #unbindInstanceForCurentThread()}归还实例
	 * @param obj
	 */
	public void returnInstance(T obj) {
		T instance = localInstance.get();
		if(null == instance || obj != instance){
			GenericObjectPool<T> pool = lazyPool.get();
			BRIDEG_LOGGER.log("return instance {},c:{},d:{},b:{} r:{},i:{},a:{}",
					factory.getType().getName(),
					pool.getCreatedCount(),
					pool.getDestroyedCount(),
					pool.getBorrowedCount(),
					pool.getReturnedCount(),
					pool.getNumIdle(),
					pool.getNumActive());
			pool.returnObject(obj);
		}
	}
	
	/**
	 * 绑定实例到当前线程<br>
	 * 为当前线程申请一个为当前线程专用的实例,直到调用{@link #unbindInstanceForCurentThread()}时释放
	 */
	public void bindInstanceForCurentThread(){
		if(null == localInstance.get()){
			GenericObjectPool<T> pool = lazyPool.get();
			BRIDEG_LOGGER.log("bind instance {},c:{},d:{},b:{} r:{},i:{},a:{}",
					factory.getType().getName(),
					pool.getCreatedCount(),
					pool.getDestroyedCount(),
					pool.getBorrowedCount(),
					pool.getReturnedCount(),
					pool.getNumIdle(),
					pool.getNumActive());
			localInstance.set(applyInstance());
		}
	}
	/**
	 * 解除绑定在当前线程上的实例归还到资源池 
	 */
	public void unbindInstanceForCurentThread(){
		T instance ;
		if(null != (instance = localInstance.get())){
			GenericObjectPool<T> pool = lazyPool.get();
			BRIDEG_LOGGER.log("unbind instance {},c:{},d:{},b:{} r:{},i:{},a:{}",
					factory.getType().getName(),
					pool.getCreatedCount(),
					pool.getDestroyedCount(),
					pool.getBorrowedCount(),
					pool.getReturnedCount(),
					pool.getNumIdle(),
					pool.getNumActive());
			localInstance.remove();
			returnInstance(instance);
		}
	}
	/**
	 * 强制删除当前线程保存的{@link BaseJniBridge}实例,谨用
	 * @return 返回当前线程保存的{@link BaseJniBridge}实例或{@code null},返回的实例需要调用方自己执行{@link #returnInstance(BaseJniBridge)}还回资源池
	 */
	public T removeLocalInstance(){
		T instance = localInstance.get();
		localInstance.remove();
		return instance;
	}
	/**
	 * 强制删除指定线程保存的{@link BaseJniBridge}实例
	 * @param thread
	 */
	public void removeLocalInstanceOf(Thread thread){
		removeThreadLocal(localInstance,thread);
	}
	/**
	 * 指定当前线程的算法实例
	 * @param instance 算法实例,为{@code null}删除当前线程算法实例
	 */
	public void setLocalInstance(T instance){
		if(null != instance){
			localInstance.set(instance);
		}else{
			localInstance.remove();
		}
	}
	public T getLocalInstance(){
		return localInstance.get();
	}
	
	/**
	 * 反射方式删除指定线程的线程局部变量
	 * @param local
	 * @param thread
	 */
	static void removeThreadLocal(ThreadLocal<?> local,Thread thread){
		if(local != null && thread != null){
			try {
				Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");			
				threadLocalsField.setAccessible(true);
				Object threadLocalTable = threadLocalsField.get(thread);
				if(threadLocalTable != null){
					Method removeMethod = threadLocalTable.getClass().getDeclaredMethod("remove", ThreadLocal.class);
					removeMethod.setAccessible(true);
					removeMethod.invoke(threadLocalTable, local);
				}
			} catch (RuntimeException e) {
				throw e;
			} catch (InvocationTargetException e) {
				throw new RuntimeException(e.getTargetException());
			}catch (Exception e) {
				throw new RuntimeException(e);
			} 
		}
	}
	public GenericObjectPool<T> getPool(){
		return lazyPool.get();
	}
	/**
	 * 调用{@link GenericObjectPool#clear()}安全清除资源池中所有资源对象<br>
	 * 通过{@link GenericObjectPool#getNumIdle()}和{@link GenericObjectPool#getNumActive()}获取资源池中空闲和激活实例的数量，
	 * 当资源池中有空闲资源对象时就执行{@link GenericObjectPool#clear()},当资源池中还有激活实例时线程睡眠一定时间(200ms),再执行{@link GenericObjectPool#clear()},
	 * 直到所有的实例都被销毁
	 * @param pool 资源池对象
	 * @param timeoutMills 等待超时(毫秒),小于0时无限等待.
	 */
	public static void clearAll(GenericObjectPool<?> pool, long timeoutMills){
		if(null != pool){
			synchronized (pool) {
				final long sleepMills = 200; 
				int watiCount = timeoutMills < 0 ? Integer.MAX_VALUE : (int)((timeoutMills + sleepMills - 1) / sleepMills);

				try {
					while(pool.getNumIdle() > 0 || pool.getNumActive() > 0){
						do{
							
							BRIDEG_LOGGER.log("clear pool {} i:{} a:{}",  getBridgeName(pool),pool.getNumIdle(), pool.getNumActive());
							pool.clear();
							if(pool.getNumActive() > 0){
								if(watiCount-- >= 0){
									BRIDEG_LOGGER.log("clear pool {} time out pool i:{} a:{}",  getBridgeName(pool),pool.getNumIdle(), pool.getNumActive());
									return;
								}
								BRIDEG_LOGGER.log("sleep for next clear pool {} i:{} a:{}",  getBridgeName(pool),pool.getNumIdle(), pool.getNumActive());
								Thread.sleep(sleepMills);
							}
						}while(pool.getNumIdle() > 0);
					}
				} catch (InterruptedException e) {
					// DO NOTHING
				}
			}
		}
	}
	/**
	 * 安全清除资源池中所有资源对象<br>
	 * 参见 {@link #clearAll(GenericObjectPool, long)}
	 * @param timeoutMills 等待超时(毫秒),小于0时无限等待.
	 * @param pools 资源池对象
	 */
	public static void clearAll(long timeoutMills,GenericObjectPool<?> ...pools){
		if(null != pools && pools.length > 0){
			for(GenericObjectPool<?> pool:pools){
				try {
					clearAll(pool,timeoutMills);
				} catch (Throwable e) {
					BRIDEG_LOGGER.log("clearAll {} ERROR:{}:{}", getBridgeName(pool),e.getClass().getName(),e.getMessage());
				}
			}	
		}
	}
	/**
	 * (异步方式)安全清除资源池中所有资源对象<br>
	 * @param timeoutMills 等待超时(毫秒),小于0时无限等待.
	 * @param onDone  回调函数,当清除结束时调用,为{@code null}忽略
	 * @param pools 资源池对象
	 * @see #clearAll(long, GenericObjectPool[])
	 */
	public static void clearAll(final long timeoutMills,final Runnable onDone,final GenericObjectPool<?> ...pools){
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						clearAll(timeoutMills,pools);	
					} finally {
						if(onDone != null){
							onDone.run();
						}
					}
				}
			},"clear-bridge-pool").start();
	}
	
	private static String getBridgeName(GenericObjectPool<?>pool){
		PooledObjectFactory<?> factory = pool.getFactory();
		String bridgeName = "";
		if(factory instanceof BaseInstanceFactory){
			bridgeName = ((BaseInstanceFactory<?>)factory).getType().getSimpleName();
		}
		return bridgeName;
	}
	/**
	 * 清理{@code bridgePools}中所有无效引用,返回所有有效的{@link GenericObjectPool}实例
	 * @return 所有 {@link GenericObjectPool}实例
	 */
	private synchronized static GenericObjectPool<?>[] getAllLivePools(){
		LinkedList<GenericObjectPool<?>> livePools = new LinkedList<>();
		// 遍历 bridgePools 中所有实例,清除无效引用将有效实例保存在 livePools
		for(Iterator<Entry<Class<?>, WeakReference<? extends BridgePool<?>>>> itor = bridgePools.entrySet().iterator();itor.hasNext();){
			Entry<Class<?>, WeakReference<? extends BridgePool<?>>> entry = itor.next();
			WeakReference<? extends BridgePool<?>> ref = entry.getValue();
			BridgePool<? extends BaseJniBridge> pool = ref.get();
			if(null == pool){
				// 删除无效的引用
				itor.remove();
			}else{
				livePools.add(pool.getPool());
			}
		}
		return livePools.toArray(new GenericObjectPool<?>[livePools.size()]);
	}
	/**
	 * (异步方式)安全清除所有{@link BridgePool}实例资源池中所有资源对象<br>
	 * 构造方法{@link #BridgePool(Class, GenericObjectPoolConfig)}会保存所有构造实例<br>
	 * 参见 {@link #clearAll(long, Runnable, GenericObjectPool...)}
	 * @param timeoutMills 等待超时(毫秒),小于0时无限等待.
	 * @param onDone  回调函数，当清除结束时调用,为{@code null}忽略
	 */
	public static void clearAllBridgePools(long timeoutMills,Runnable onDone){
		clearAll(timeoutMills, onDone, getAllLivePools());
	}
	/**
	 * 安全清除所有{@link BridgePool}实例资源池中所有资源对象<br>
	 * 构造方法{@link #BridgePool(Class, GenericObjectPoolConfig)}会保存所有构造实例<br>
	 * @param timeoutMills 等待超时(毫秒),小于0时无限等待.
	 * @see #clearAll(long, GenericObjectPool[])
	 */
	public static void clearAllBridgePools(long timeoutMills){
		clearAll(timeoutMills, getAllLivePools());
	}
	/**
	 * 创建同步锁对象<br>
	 * {@link #applyInstance()}方法会阻塞直到调用{@link SyncLock#unlock()},如果超时则抛出{@link net.facelib.jni.SyncLock.SyncLockTimeoutException}异常
	 * @param timeoutMills 超时时间(毫秒),小于等于0使用默认值{@link SyncLock#DEFAULT_WAIT_INTERNAL_MILLS},等于0则无限等待
	 * @return 返回同步锁对象
	 */
	public synchronized static SyncLock enableSyncLock(long timeoutMills) {
		// 防止已经有阻塞线程,所以先打开原有锁对象
		BridgePool.syncLock.unlock();
		BridgePool.syncLock = new SyncLock(timeoutMills);
		return BridgePool.syncLock;
	}
}
